
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include <linux/mod_devicetable.h>
#include <linux/of_device.h>

#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>


static int major;
static struct class *dht11_class;
static struct gpio_desc *dht11_gpiod;
static unsigned char dht11_data[5];
//static int irq;



// static DECLARE_WAIT_QUEUE_HEAD(dht11_wait_queue);  			 /*创建并初始化等待队列*/

static int dht11_open(struct inode *node, struct file *file)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static void dht11_start(void)
{
	u64 pre_time_ns = 0;
	u64 now_time_ns = 0;

	/*设置引脚为输入输出，并拉高电平20ms,同时作为复位信号*/
	gpiod_direction_output(dht11_gpiod, 1);
	mdelay(20);
	
	/*拉低电平20ms*/
	gpiod_set_value(dht11_gpiod, 0);
	mdelay(20);

	/*拉高电平30us*/
	gpiod_set_value(dht11_gpiod, 1);
	pre_time_ns = ktime_get_ns();
	while(1)
	{
		udelay(1);
		now_time_ns = ktime_get_ns();
		if(now_time_ns - pre_time_ns > 30000)
			break;
	}
	/*设置引脚为输入引脚*/
	gpiod_direction_input(dht11_gpiod);	
	
}



static int wait_for_dht11_ready(void)
{
	int timeout = 100000;

	/*等待低电平*/
	
	while (gpiod_get_value(dht11_gpiod) && timeout)
	{
		udelay(1);
		timeout--;		
	}
	if (timeout == 0)
	{
		printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;
	}

	/*等待高电平*/
	timeout = 10000;
	while (!gpiod_get_value(dht11_gpiod) && timeout)
	{
		udelay(1);
		timeout--;
	}
	
	if (timeout == 0)
	{
		printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;
	}
	
	/*等待低电平*/
	timeout = 10000;
	while (gpiod_get_value(dht11_gpiod) && timeout)
	{
		udelay(1);
		timeout--;		
	}
	if (timeout == 0)
	{
		printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;
	}
	
	return 0;
}


static int dht11_read_byte(void)
{
	u64 time_ns = 0;
	int i;
	int timeout;
	int data = 0;
	for (i = 0; i < 8; i++ )
	{
		/*等待高电平*/
		timeout = 10000;
		while(!gpiod_get_value(dht11_gpiod) && timeout)
		{
			udelay(1);
			timeout--;
		}
		
		if(timeout == 0)
		{
			printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
			return -EAGAIN;
		}
		
		time_ns = ktime_get_ns();
		
		/*等待低电平*/
		timeout = 10000;
		while(gpiod_get_value(dht11_gpiod) && timeout)
		{
			udelay(1);
			timeout--;		
		}
		
		if(timeout == 0)
		{
			printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
			return -EAGAIN;
		}
		
		time_ns = ktime_get_ns() - time_ns;
		
		if (time_ns > 40000)
			data = (data << 1) | 1;
		else
			data = (data << 1);
	}

	return data;
}




static ssize_t dht11_read(struct file *file, char __user *buf, size_t size, loff_t *offsize)
{
	int sum = 0;
	int ret;
	int i;
	unsigned long flags;
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	local_irq_save(flags);
	
	/*主机发送开始信号*/
	dht11_start();

	/*等待从机回应*/
	ret = wait_for_dht11_ready();
	if (ret)	
	{
		local_irq_restore(flags);
		printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;
	}

	/*读取数据*/
	for (i = 0; i < 5; i++)
	{
		dht11_data[i] = dht11_read_byte();
		if (dht11_data[i] < 0)
		{
			local_irq_restore(flags);			
			return -EAGAIN;
		}
	}
	/*判断校验和*/
	sum = dht11_data[0] + dht11_data[1] + dht11_data[2] + dht11_data[3];
	
	if (sum == dht11_data[4])
	{
		local_irq_restore(flags);
		ret = copy_to_user(buf, dht11_data, 4);
		return 4;	
	}
	else
	{
		local_irq_restore(flags);
		return -EAGAIN;
	}

}

static int dht11_close(struct inode *node, struct file *file)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct file_operations dht11_ops = {
	.owner  = THIS_MODULE,
	.open    = dht11_open,
	.release = dht11_close,
	.read    = dht11_read,
};

#if 0
static irqreturn_t dht11_isr(int num, void *dev)
{
	int val = gpiod_get_value(dht11_echo);
	if (val)           /*回应引脚高电平*/
		dht11_data_ns = ktime_get_ns();
	else
	{
		dht11_data = ktime_get_ns() - dht11_data_ns;   /*回应引脚高电平*/
		wake_up(&dht11_wait_queue);
	}
		
	return IRQ_HANDLED;
}

#endif



static struct of_device_id of_dht11_match_table[] = {
	{ .compatible = "zmq,dth11", },
	{ },
};

static int dht11_probe(struct platform_device *pdev)
{

	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	dht11_gpiod = gpiod_get(&pdev->dev, NULL, GPIOD_OUT_HIGH);

	//irq = gpiod_to_irq(dht11_echo);
	//request_irq(irq, dht11_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "zmq_dht11_irq", NULL);
	
	device_create(dht11_class, NULL, MKDEV(major, 0), NULL, "zmq_dht11");
	return 0;
	
}


static int dht11_remove(struct platform_device *dev)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(dht11_class, MKDEV(major, 0));
	gpiod_put(dht11_gpiod);   //释放gpio
	//gpiod_put(dht11_echo);
	//free_irq(irq, NULL);
	
	return 0;
}


static struct platform_driver dht11_platform_drv = {
	.probe = dht11_probe,
	.remove = dht11_remove,
	.driver = 	{
		.name = "dht11",
		.of_match_table = of_dht11_match_table,
	},

};



static  int  dht11_init(void)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	major = register_chrdev(0, "zmq_dht11_chrdrv", &dht11_ops);
	dht11_class = class_create(THIS_MODULE, "zmq_dht11_class");
	if(IS_ERR(dht11_class))
	{
		unregister_chrdev(major, "zmq_dht11_chrdrv");
		return PTR_ERR(dht11_class);
	}
	platform_driver_register(&dht11_platform_drv);
	
	return 0;
}


static void dht11_exit(void)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);

	platform_driver_unregister(&dht11_platform_drv);
	class_destroy(dht11_class);
	unregister_chrdev(major, "zmq_dht11_chrdev");
}


module_init(dht11_init);
module_exit(dht11_exit);
MODULE_LICENSE("GPL");






